from datetime import datetime, timedelta, timezone
from typing import Annotated
from fastapi.security import OAuth2PasswordBearer
import jwt
from passlib.context import CryptContext
from jwt.exceptions import InvalidTokenError
from fastapi import HTTPException, status, Depends
from pydantic import BaseModel

from models.user import User
from utils.config import config
from utils.mongo_client import MongoDBClient


class Token(BaseModel):
    access_token: str
    token_type: str


class TokenData(BaseModel):
    username: str | None = None


# 创建MongoDB客户端
client = MongoDBClient()

# 创建生成Token配置
SECRET_KEY = config.secret_key
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = config.token_expire_minutes
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# 创建OAuth2密码Bearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def get_password_hash(password) -> str:
    """生成密码的哈希值"""
    return pwd_context.hash(password)


def verify_password(plain_password, hashed_password) -> bool:
    """验证明文密码与哈希密码是否匹配"""
    return pwd_context.verify(plain_password, hashed_password)


def search_user(username: str) -> dict | None:
    """查询用户

    Args:
        username (str): 用户名

    Returns:
        (dict,None): 用户信息
    """

    return client.find_one("users", {"username": username})


def create_user(user: User) -> dict:
    """创建用户

    Args:
        user (User): _description_

    """
    return client.insert_one("users", user)


def validator_user(username: str, password: str) -> HTTPException:
    """验证用户身份信息

    Args:
        username (str): 用户名
        password (str): 密码
    """
    user = search_user(username)
    if user:
        if user["password"] == password:
            return {"access_token": username, "token_type": "bearer"}
        else:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect password")
    else:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="User not found")


async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):

    #  验证token失败信息
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )

    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except InvalidTokenError:
        raise credentials_exception

    # 查询用户信息，防止用户信息被删除后还能通过token访问
    user = search_user(username=token_data.username)
    if user is None:
        raise credentials_exception
    return user


def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt
